import asyncio
from typing import List, Dict

from fastapi import FastAPI, Depends, WebSocket, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session
from starlette.websockets import WebSocketDisconnect
import json
from datetime import datetime

from database import get_db, User, Content
from config import settings
from routers import auth_router, payment
from routers.interests import router as interests_router
from routers.search import router as search_router
from routers.content import router as content_router
from routers.goods import router as goods_router
from routers.orders import router as orders_router

from view.dingtalk_oauth import router as dingtalk_oauth_router
from view.identity import router as identity_router
from view.coze import router as coze_router
from view.dingdan import router as order_queue_router
from view.websocket import (
    websocket_chat_consumer,
    service_connections,
    user_connections,
    consultation_requests,
    send_json,
    get_room_id,
    match_service
)
from patterns.observer import init_default_listeners
from services.scheduler import init_scheduler, shutdown_scheduler
from threading import Thread

app = FastAPI(
    title="小红书App API",
    description="仿小红书社区应用 - FastAPI后端 (集成观察者模式、定时任务、Redis分布式锁)",
    version="1.0.0"
)

# 初始化事件监听器
init_default_listeners()

# 初始化定时任务调度器
@app.on_event("startup")
async def startup_event():
    """应用启动时初始化定时任务"""
    init_scheduler()
    print("[OK] 定时任务调度器已启动")
    # 启动RabbitMQ订单消费者（后台线程）
    try:
        import order_consumer
        def _run_consumer():
            try:
                order_consumer.main()
            except Exception as e:
                print(f"[错误] 订单消费者退出: {e}")
        t = Thread(target=_run_consumer, daemon=True)
        t.start()
        print("[OK] 订单消费者已后台启动")
    except Exception as e:
        print(f"[警告] 订单消费者启动失败: {e}")


@app.on_event("shutdown")
async def shutdown_event():
    """应用关闭时清理资源"""
    print("[关闭] 开始清理资源...")
    
    # 关闭所有WebSocket连接
    try:
        # 关闭用户连接
        user_count = len(user_connections)
        for user_id, websocket in list(user_connections.items()):
            try:
                await websocket.close(code=1001, reason="Server shutdown")
            except:
                pass
        user_connections.clear()
        print(f"[关闭] 已关闭 {user_count} 个用户连接")
    except Exception as e:
        print(f"[关闭] 关闭用户连接时出错: {e}")
    
    try:
        # 关闭客服连接
        service_count = len(service_connections)
        for service_id, websocket in list(service_connections.items()):
            try:
                await websocket.close(code=1001, reason="Server shutdown")
            except:
                pass
        service_connections.clear()
        print(f"[关闭] 已关闭 {service_count} 个客服连接")
    except Exception as e:
        print(f"[关闭] 关闭客服连接时出错: {e}")
    
    try:
        # 关闭其他WebSocket连接
        active_count = len(active_connections)
        for websocket in list(active_connections):
            try:
                await websocket.close(code=1001, reason="Server shutdown")
            except:
                pass
        active_connections.clear()
        print(f"[关闭] 已关闭 {active_count} 个其他连接")
    except Exception as e:
        print(f"[关闭] 关闭其他连接时出错: {e}")
    
    # 关闭定时任务
    try:
        shutdown_scheduler()
        print("[关闭] 定时任务调度器已关闭")
    except Exception as e:
        print(f"[关闭] 关闭定时任务时出错: {e}")
    
    print("[关闭] 资源清理完成")

# 配置CORS跨域
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:3000",
        "http://localhost:3001",
        "http://localhost:5173",
        "http://127.0.0.1:3000",
        "http://127.0.0.1:3001",
        "http://127.0.0.1:5173",
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["*"],
)

# 添加调试中间件：打印请求头和错误（添加错误处理）
@app.middleware("http")
async def log_requests(request, call_next):
    try:
        # 只在需要时打印（避免日志过多）
        if "/api/" in str(request.url) and request.method != "OPTIONS":
            auth_header = request.headers.get("authorization") or request.headers.get("Authorization")
            if auth_header:
                print(f"[AUTH] {request.method} {request.url.path} - Authorization: {auth_header[:40]}...")
            else:
                print(f"[WARN] {request.method} {request.url.path} - No Authorization header")
    except Exception as e:
        print(f"[ERROR] Middleware error: {e}")
        import traceback
        traceback.print_exc()
    
    try:
        response = await call_next(request)
        return response
    except Exception as e:
        error_type = type(e).__name__
        error_msg = str(e)
        print(f"\n{'='*60}")
        print(f"[ERROR] Request processing error on {request.method} {request.url.path}")
        print(f"   Error Type: {error_type}")
        print(f"   Error Message: {error_msg}")
        print(f"{'='*60}")
        import traceback
        traceback.print_exc()
        print(f"{'='*60}\n")
        raise

# ============ WebSocket 连接管理 ============
# 存储所有活跃的 WebSocket 连接（用于广播消息）
active_connections: List[WebSocket] = []
# 存储客户端ID与连接的映射（用于定向推送消息给特定客户端）
# key: 客户端ID（通常是用户ID），value: WebSocket 连接对象
client_connections: Dict[str, WebSocket] = {}


# ============ 消息推送函数 ============
async def broadcast(message: str):
    """
    广播消息给所有连接的客户端
    
    用途：
    - 系统公告
    - 活动通知
    - 全局消息推送
    
    参数:
        message: 要广播的消息内容（字符串）
    """
    for connection in active_connections:
        await connection.send_text(message)


async def send_to_client(client_id: str, message: str):
    """
    定向推送消息给指定客户端
    
    用途：
    - 个人通知（点赞、评论等）
    - 私信推送
    - 定向消息
    
    参数:
        client_id: 客户端ID（通常是用户ID）
        message: 要推送的消息内容（字符串）
    
    返回:
        无（如果客户端不存在，静默失败）
    """
    if client_id in client_connections:
        await client_connections[client_id].send_text(message)


@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: str):
    """
    WebSocket 连接端点
    
    路径: ws://127.0.0.1:8000/ws/{client_id}
    
    参数:
        client_id: 客户端ID（通常是用户ID），用于标识不同的客户端
    
    工作流程:
    1. 接受客户端连接请求
    2. 将连接存储到连接池中
    3. 进入消息循环，持续接收和发送消息
    4. 客户端断开时清理连接
    
    说明:
    - 每个客户端可以建立独立的 WebSocket 连接
    - 服务器可以根据 client_id 进行定向推送
    - 支持广播消息给所有客户端
    """
    # ============ 连接建立 ============
    # 接受客户端的 WebSocket 连接请求
    await websocket.accept()
    
    # 将连接添加到连接池中
    active_connections.append(websocket)           # 添加到全局连接列表（用于广播）
    client_connections[client_id] = websocket      # 添加到客户端映射（用于定向推送）
    print(f"客户端 {client_id} 已连接，当前连接数：{len(active_connections)}")

    try:
        # ============ 消息循环 ============
        # 持续接收客户端发送的消息，直到连接断开
        while True:
            # 接收客户端发送的文本消息（阻塞等待）
            data = await websocket.receive_text()
            print(f"收到客户端 {client_id} 的消息：{data}")

            # ============ 消息处理选项 ============
            # 1. 回复当前客户端（单独回应）
            # 用于：确认消息接收、响应客户端请求等
            await websocket.send_text(f"服务器已收到你的消息：{data}")

            # 2. 广播给所有客户端（可选，默认注释）
            # 用于：群聊、系统公告等场景
            # await broadcast(f"客户端 {client_id} 说：{data}")

            # 3. 模拟服务器主动推送（5秒后推送一条消息）
            # 注意：这是测试代码，实际业务中应根据具体需求实现
            # 例如：定时推送、事件触发推送等
            await asyncio.sleep(5)
            await websocket.send_text(f"服务器主动推送：你5秒前发送了「{data}」")

    except WebSocketDisconnect:
        # ============ 连接断开处理 ============
        # 客户端主动断开连接时触发此异常
        # 清理连接池，释放资源
        
        # 从全局连接列表中移除
        if websocket in active_connections:
            active_connections.remove(websocket)
        
        # 从客户端映射中移除
        client_connections.pop(client_id, None)
        
        print(f"客户端 {client_id} 已断开，当前连接数：{len(active_connections)}")


# ============ HTTP 推送接口 ============
@app.post("/push/{client_id}")
async def push_to_client(client_id: str, message: str):
    """
    HTTP 接口：触发 WebSocket 消息推送
    
    路径: POST /push/{client_id}?message=xxx
    或: POST /push/{client_id}
        Body: {"message": "xxx"}
    
    用途:
    - 外部系统触发推送（如定时任务、后台管理）
    - 业务逻辑触发推送（如点赞通知、评论通知）
    - 测试和调试
    
    参数:
        client_id: 目标客户端ID（通常是用户ID）
        message: 要推送的消息内容（字符串）
    
    返回:
        - 成功: {"status": "success", "message": "..."}
        - 失败: {"status": "error", "message": "..."}
    
    说明:
    - 如果客户端未连接，返回错误信息
    - 此接口主要用于测试和后台推送，前端一般通过 WebSocket 直接通信
    """



    # 检查客户端是否已连接
    if client_id in client_connections:
        # 客户端已连接，推送消息
        await send_to_client(client_id, f"管理员推送：{message}")
        return {"status": "success", "message": f"已向 {client_id} 推送消息"}
    else:
        # 客户端未连接，返回错误
        return {"status": "error", "message": f"客户端 {client_id} 未连接"}





# ============ 客服系统连接管理 ============
# 
# 全局连接管理字典，用于维护所有活跃的WebSocket连接
# 这些连接在内存中存储，应用重启后会丢失（生产环境建议使用Redis等持久化方案）

# 客服连接管理字典
# 键: 客服ID（service_id），值: 客服的WebSocket连接对象
# 用途: 存储所有在线客服的连接，用于接收咨询请求通知
service_connections: Dict[str, WebSocket] = {}

# 用户连接管理字典
# 键: 用户ID（user_id），值: 用户的WebSocket连接对象
# 用途: 存储所有在线用户的连接，用于接收咨询状态更新通知
user_connections: Dict[str, WebSocket] = {}

# 聊天室管理字典
# 键: 聊天室ID（room_id），格式为 "{user_id}_{service_id}"
# 值: 包含用户和客服WebSocket连接的字典，格式为 {"user": websocket, "service": websocket}
# 用途: 存储每个聊天室的双向连接，实现用户和客服的实时聊天
# 示例: {"user_123_service_1001": {"user": <WebSocket>, "service": <WebSocket>}}
chat_rooms: Dict[str, Dict[str, WebSocket]] = {}

# 咨询请求列表字典
# 键: 客服ID（service_id）
# 值: 该客服的所有咨询请求列表，每个请求包含:
#     - user_id: 用户ID
#     - service_id: 客服ID
#     - created_at: 创建时间
#     - room_id: 聊天室ID
#     - status: 状态（pending/accepted/rejected）
# 用途: 管理咨询请求队列，前端可以通过HTTP接口或WebSocket获取待处理请求
consultation_requests: Dict[str, List[Dict]] = {}


# ============ 客服系统工具函数 ============

async def send_json(websocket: WebSocket, data: dict):
    """
    发送JSON格式的消息到WebSocket连接
    
    参数:
        websocket: 目标WebSocket连接对象
        data: 要发送的数据字典，会自动转换为JSON字符串
    
    说明:
        - 使用 ensure_ascii=False 保持中文字符正常显示
        - 异步函数，需要使用 await 调用
    """
    await websocket.send_text(json.dumps(data, ensure_ascii=False))  # 将字典转换为JSON字符串并发送，ensure_ascii=False保持中文正常显示


def get_room_id(user_id: str, service_id: str) -> str:
    """
    生成聊天室唯一ID
    参数:
        user_id: 用户ID
        service_id: 客服ID
    
    返回:
        聊天室ID，格式: "{user_id}_{service_id}"
    
    说明:
        - 同一用户和客服的聊天室ID是固定的
        - 房间ID确保每个用户-客服对都有唯一的聊天空间
    """
    return f"{user_id}_{service_id}"  # 返回格式化的聊天室ID，格式: user_id_service_id


def match_service(user_id: str) -> str:
    """
    为用户匹配可用的客服
    
    参数:
        user_id: 用户ID
    
    返回:
        匹配到的客服ID
    

    """
    # 如果有在线客服，返回第一个
    if service_connections:  # 检查是否有在线客服
        return list(service_connections.keys())[0]  # 返回第一个在线客服的ID
    # 如果没有在线客服，返回默认客服ID
    return "service_1001"  # 返回默认客服ID（当没有在线客服时）


# ============ HTTP接口：开始咨询 ============

@app.post("/api/customer-service/start-consult")
async def start_consultation(request: Request):
    """
    用户点击"开始咨询"按钮
    1. 根据用户ID匹配客服（写死）
    2. 创建聊天室
    3. 通过WebSocket通知客服有新咨询请求
    """
    try:
        data = await request.json()  # 解析请求体JSON数据
        user_id = str(data.get("user_id"))  # 获取并转换为字符串格式的用户ID
        
        if not user_id:
            raise HTTPException(status_code=400, detail="用户ID不能为空")  # 用户ID为空时抛出400错误
        
        # 匹配客服（暂时写死）
        service_id = match_service(user_id)  # 根据用户ID匹配可用的客服
        room_id = get_room_id(user_id, service_id)  # 生成聊天室唯一ID，格式: user_id_service_id
        
        # 创建咨询请求信息
        request_info = {
            "user_id": user_id,  # 用户ID
            "service_id": service_id,  # 客服ID
            "room_id": room_id,  # 聊天室ID
            "created_at": datetime.now().isoformat(),  # 创建时间（ISO格式）
            "status": "pending"  # 状态：pending(待处理), accepted(已接受), rejected(已拒绝)
        }
        
        # 添加到咨询请求列表
        if service_id not in consultation_requests:
            consultation_requests[service_id] = []  # 如果该客服没有请求列表，先创建空列表
        
        # 检查是否已存在相同的请求
        existing = [r for r in consultation_requests[service_id] if r["user_id"] == user_id and r["status"] == "pending"]  # 查找该用户是否有待处理的请求
        if existing:
            # 如果已有待处理的请求，返回已有信息
            return {
                "code": 200,  # HTTP状态码
                "message": "咨询请求已存在",  # 提示信息
                "data": existing[0]  # 返回已存在的请求数据
            }
        
        consultation_requests[service_id].append(request_info)  # 将新请求添加到该客服的请求列表中
        
        # 实时推送：通过WebSocket通知客服有新咨询请求
        if service_id in service_connections:  # 检查客服是否在线
            await send_json(service_connections[service_id], {  # 向客服发送新咨询通知
                "type": "new_consultation",  # 消息类型：新咨询请求
                "data": request_info  # 请求详细信息
            })
            print(f"[实时推送] 已向客服 {service_id} 推送新咨询请求：用户 {user_id}")  # 日志输出
        else:
            print(f"[警告] 客服 {service_id} 未连接，无法推送咨询请求")  # 客服不在线时的警告
        
        # 实时推送：通知用户（如果用户已连接）
        if user_id in user_connections:  # 检查用户是否在线
            await send_json(user_connections[user_id], {  # 向用户发送请求创建成功的通知
                "type": "consultation_created",  # 消息类型：咨询已创建
                "data": request_info  # 请求详细信息
            })
            print(f"[实时推送] 已向用户 {user_id} 推送咨询请求创建成功")  # 日志输出
        
        return {
            "code": 200,  # HTTP状态码
            "message": "咨询请求已创建",  # 成功提示信息
            "data": request_info  # 返回创建的请求数据
        }
        
    except Exception as e:
        print(f"[错误] 创建咨询请求失败: {e}")  # 打印错误日志
        raise HTTPException(status_code=500, detail=str(e))  # 抛出500服务器错误


# ============ WebSocket：客服连接 ============

@app.websocket("/ws/service/{service_id}")
async def service_websocket(websocket: WebSocket, service_id: str):
    """
    客服WebSocket连接
    用于接收新咨询请求通知和实时推送
    
    实时推送机制：
    1. 客服连接时自动推送所有待处理的咨询请求
    2. 新咨询请求实时推送给客服
    3. 所有操作都是服务器主动推送，无需客服主动刷新
    """
    await websocket.accept()  # 接受WebSocket连接请求
    
    # 存储客服连接
    service_connections[service_id] = websocket  # 将客服连接存储到全局字典中
    consultation_requests[service_id] = consultation_requests.get(service_id, [])  # 初始化该客服的咨询请求列表（如果不存在）
    
    print(f"[客服连接] {service_id} 已连接，当前在线客服数：{len(service_connections)}")  # 输出连接日志
    
    # ============ 实时推送：客服连接时自动推送所有待处理请求 ============
    pending_requests = [r for r in consultation_requests.get(service_id, []) if r["status"] == "pending"]  # 筛选出待处理的请求
    if pending_requests:  # 如果有待处理请求
        await send_json(websocket, {  # 实时推送：发送给客服
            "type": "consultation_list",  # 消息类型：咨询列表
            "data": pending_requests  # 待处理请求列表
        })
        print(f"[实时推送] 已向客服 {service_id} 推送 {len(pending_requests)} 个待处理咨询请求")  # 日志输出
    # ============ 实时推送：客服连接时自动推送所有待处理请求（结束） ============
    
    try:
        while True:  # 消息循环，持续接收客服发送的消息
            data = await websocket.receive_text()  # 接收文本消息
            message = json.loads(data)  # 解析JSON格式的消息
            message_type = message.get("type")  # 获取消息类型
            
            if message_type == "accept_consultation":  # 如果消息类型是接受咨询请求
                # 客服接受咨询请求
                room_id = message.get("room_id")  # 获取聊天室ID
                # 更新请求状态
                for req in consultation_requests.get(service_id, []):  # 遍历该客服的所有请求
                    if req["room_id"] == room_id:  # 找到对应的请求
                        req["status"] = "accepted"  # 将状态更新为已接受
                        # 实时推送：通知用户咨询已被接受
                        user_id = req["user_id"]  # 获取用户ID
                        if user_id in user_connections:  # 如果用户在线
                            await send_json(user_connections[user_id], {  # 向用户发送通知
                                "type": "consultation_accepted",  # 消息类型：咨询已接受
                                "data": {"room_id": room_id, "service_id": service_id}  # 聊天室ID和客服ID
                            })
                            print(f"[实时推送] 已向用户 {user_id} 推送咨询已被客服 {service_id} 接受")  # 日志输出
                        break  # 找到后退出循环
            
            print(f"[客服消息] {service_id}: {message}")  # 打印收到的消息日志
            
    except WebSocketDisconnect:  # 捕获WebSocket断开异常
        service_connections.pop(service_id, None)  # 从连接字典中移除该客服
        print(f"[客服断开] {service_id} 已断开，当前在线客服数：{len(service_connections)}")  # 输出断开日志


# ============ WebSocket：用户连接 ============

@app.websocket("/ws/user/{user_id}")
async def user_websocket(websocket: WebSocket, user_id: str):
    """
    用户WebSocket连接
    用于接收咨询请求状态更新和实时推送
    
    实时推送机制：
    1. 用户连接时自动创建咨询请求并推送
    2. 客服接受/拒绝时实时推送状态更新
    3. 所有操作都是服务器主动推送，无需用户主动操作
    """
    await websocket.accept()  # 接受WebSocket连接请求
    
    # 存储用户连接
    user_connections[user_id] = websocket  # 将用户连接存储到全局字典中
    
    print(f"[用户连接] {user_id} 已连接，当前在线用户数：{len(user_connections)}")  # 输出连接日志
    
    # ============ 实时推送：用户连接时自动创建咨询请求 ============
    try:
        # 匹配客服（自动匹配）
        service_id = match_service(user_id)  # 根据用户ID匹配可用的客服
        room_id = get_room_id(user_id, service_id)  # 生成聊天室唯一ID
        
        # 检查是否已存在相同的请求
        existing = None  # 初始化existing变量
        if service_id in consultation_requests:  # 如果该客服有请求列表
            existing = [r for r in consultation_requests[service_id] if r["user_id"] == user_id and r["status"] == "pending"]  # 查找是否有待处理的请求
            if existing:  # 如果已存在待处理请求
                existing = existing[0]  # 取第一个
        
        if existing:  # 如果已存在待处理请求
            # 推送已有请求信息
            await send_json(websocket, {  # 向用户发送响应
                "type": "consultation_created",  # 消息类型：咨询已创建
                "code": 200,  # HTTP状态码
                "message": "咨询请求已存在",  # 提示信息
                "data": existing  # 返回已存在的请求数据
            })
        else:  # 如果不存在，自动创建新请求
            # 创建咨询请求信息
            request_info = {
                "user_id": user_id,  # 用户ID
                "service_id": service_id,  # 客服ID
                "room_id": room_id,  # 聊天室ID
                "created_at": datetime.now().isoformat(),  # 创建时间（ISO格式）
                "status": "pending"  # 状态：pending(待处理), accepted(已接受), rejected(已拒绝)
            }
            
            # 添加到咨询请求列表
            if service_id not in consultation_requests:
                consultation_requests[service_id] = []  # 如果该客服没有请求列表，先创建空列表
            
            consultation_requests[service_id].append(request_info)  # 将新请求添加到列表
            
            # 实时推送：通过WebSocket通知客服有新咨询请求
            if service_id in service_connections:  # 检查客服是否在线
                await send_json(service_connections[service_id], {  # 向客服发送新咨询通知
                    "type": "new_consultation",  # 消息类型：新咨询请求
                    "data": request_info  # 请求详细信息
                })
                print(f"[实时推送] 已向客服 {service_id} 推送新咨询请求：用户 {user_id}")  # 日志输出
            else:
                print(f"[警告] 客服 {service_id} 未连接，无法推送咨询请求")  # 客服不在线时的警告
            
            # 实时推送：向用户发送请求创建成功的通知
            await send_json(websocket, {  # 向用户发送成功响应
                "type": "consultation_created",  # 消息类型：咨询已创建
                "code": 200,  # HTTP状态码
                "message": "咨询请求已创建",  # 成功提示信息
                "data": request_info  # 返回创建的请求数据
            })
            print(f"[实时推送] 已向用户 {user_id} 推送咨询请求创建成功")  # 日志输出
            
    except Exception as e:  # 捕获异常
        print(f"[错误] 自动创建咨询请求失败: {e}")  # 打印错误日志
        await send_json(websocket, {  # 向用户发送错误响应
            "type": "error",  # 消息类型：错误
            "code": 500,  # HTTP状态码
            "message": f"自动创建咨询请求失败: {str(e)}"  # 错误信息
        })
    # ============ 实时推送：用户连接时自动创建咨询请求（结束） ============
    
    try:
        while True:  # 消息循环，持续接收用户发送的消息
            data = await websocket.receive_text()  # 接收文本消息
            message = json.loads(data)  # 解析JSON格式的消息
            message_type = message.get("type")  # 获取消息类型
            
            # 处理创建咨询请求（保留手动创建功能，作为备用）
            if message_type == "start_consult":  # 如果消息类型是开始咨询
                try:
                    # 匹配客服（暂时写死）
                    service_id = match_service(user_id)  # 根据用户ID匹配可用的客服
                    room_id = get_room_id(user_id, service_id)  # 生成聊天室唯一ID
                    
                    # 创建咨询请求信息
                    request_info = {
                        "user_id": user_id,  # 用户ID
                        "service_id": service_id,  # 客服ID
                        "room_id": room_id,  # 聊天室ID
                        "created_at": datetime.now().isoformat(),  # 创建时间（ISO格式）
                        "status": "pending"  # 状态：pending(待处理), accepted(已接受), rejected(已拒绝)
                    }
                    
                    # 添加到咨询请求列表
                    if service_id not in consultation_requests:
                        consultation_requests[service_id] = []  # 如果该客服没有请求列表，先创建空列表
                    
                    # 检查是否已存在相同的请求
                    existing = [r for r in consultation_requests[service_id] if r["user_id"] == user_id and r["status"] == "pending"]  # 查找是否有待处理的请求
                    if existing:  # 如果已存在待处理请求
                        # 如果已有待处理的请求，返回已有信息
                        await send_json(websocket, {  # 向用户发送响应
                            "type": "consultation_created",  # 消息类型：咨询已创建
                            "code": 200,  # HTTP状态码
                            "message": "咨询请求已存在",  # 提示信息
                            "data": existing[0]  # 返回已存在的请求数据
                        })
                    else:  # 如果不存在，创建新请求
                        consultation_requests[service_id].append(request_info)  # 将新请求添加到列表
                        
                        # 实时推送：通过WebSocket通知客服有新咨询请求
                        if service_id in service_connections:  # 检查客服是否在线
                            await send_json(service_connections[service_id], {  # 向客服发送新咨询通知
                                "type": "new_consultation",  # 消息类型：新咨询请求
                                "data": request_info  # 请求详细信息
                            })
                            print(f"[实时推送] 已向客服 {service_id} 推送新咨询请求：用户 {user_id}")  # 日志输出
                        else:
                            print(f"[警告] 客服 {service_id} 未连接，无法推送咨询请求")  # 客服不在线时的警告
                        
                        # 实时推送：返回创建成功的消息
                        await send_json(websocket, {  # 向用户发送成功响应
                            "type": "consultation_created",  # 消息类型：咨询已创建
                            "code": 200,  # HTTP状态码
                            "message": "咨询请求已创建",  # 成功提示信息
                            "data": request_info  # 返回创建的请求数据
                        })
                        print(f"[实时推送] 已向用户 {user_id} 推送咨询请求创建成功")  # 日志输出
                        
                except Exception as e:  # 捕获异常
                    print(f"[错误] 创建咨询请求失败: {e}")  # 打印错误日志
                    await send_json(websocket, {  # 向用户发送错误响应
                        "type": "error",  # 消息类型：错误
                        "code": 500,  # HTTP状态码
                        "message": f"创建咨询请求失败: {str(e)}"  # 错误信息
                    })
            
            # 其他消息类型可以在这里扩展
            else:  # 其他消息类型
                print(f"[用户消息] {user_id}: {message}")  # 打印收到的消息日志
            
    except WebSocketDisconnect:  # 捕获WebSocket断开异常
        user_connections.pop(user_id, None)  # 从连接字典中移除该用户
        print(f"[用户断开] {user_id} 已断开，当前在线用户数：{len(user_connections)}")  # 输出断开日志













# ============ WebSocket：统一的聊天连接端点（支持两种格式） ============
# 注意：这个路由需要放在 /ws/chat/{room_id} 之前，以便FastAPI优先匹配

# ============ WebSocket：聊天端点 ============
# 
# WebSocket聊天功能已移至 view/websocket.py
# 在这里注册WebSocket路由

@app.websocket("/ws/chat/{group}")
async def websocket_chat(websocket: WebSocket, group: str):
    """
    WebSocket聊天连接端点
    
    连接路径: ws://host/ws/chat/{group}
    其中 group 格式: "user{user_id}" 或 "service{service_id}"
    例如: /ws/chat/user123 或 /ws/chat/service1001
    
    功能说明:
    1. 用户连接时自动创建咨询请求并推送
    2. 客服连接时自动推送所有待处理请求
    3. 实时消息转发（用户↔客服）
    4. 状态更新实时推送
    
    注意: 实际实现逻辑在 view/websocket.py 中
    """
    await websocket_chat_consumer(websocket, group)


# ============ WebSocket：聊天室 ============

@app.websocket("/ws/chat/{room_id}")
async def chat_room_websocket(websocket: WebSocket, room_id: str):
    """
    聊天室WebSocket连接端点
    
    功能说明:
        实现用户和客服之间的实时双向聊天功能。
        每个聊天室由用户和客服两个连接组成，消息在这两个连接之间转发。
    
    连接路径:
        /ws/chat/{room_id}
        其中 room_id 格式: "{user_id}_{service_id}"
        示例: /ws/chat/user_123_service_1001
    
    工作流程:
        1. 客户端连接WebSocket
        2. 发送身份标识消息（identity和client_id）
        3. 服务器验证并存储连接
        4. 如果对方已连接，通知对方有新成员加入
        5. 进入消息循环，接收和转发聊天消息
        6. 连接断开时清理资源

    """
    # ============ 步骤1: 接受WebSocket连接 ============
    # 接受客户端的WebSocket连接请求，建立连接通道
    await websocket.accept()
    
    # ============ 步骤2: 验证房间ID格式 ============
    # 解析room_id，提取user_id和service_id
    # room_id格式应为: "user_id_service_id"
    parts = room_id.split("_")
    if len(parts) < 2:
        # 房间ID格式不正确，关闭连接
        # 1008: 协议错误码，表示数据格式不符合要求
        await websocket.close(code=1008, reason="Invalid room_id format")
        return
    
    # ============ 步骤3: 初始化变量 ============
    # identity: 身份标识，用于区分是用户还是客服
    # client_id: 客户端ID，用于标识具体的用户或客服
    identity = None
    client_id = None
    
    try:
        # ============ 步骤4: 接收身份标识 ============
        # 客户端连接后必须立即发送身份标识消息
        # 消息格式: {"identity": "user" | "service", "client_id": "xxx"}
        init_message = await websocket.receive_text()
        init_data = json.loads(init_message)
        identity = init_data.get("identity")  # 获取身份: "user" 或 "service"
        client_id = init_data.get("client_id")  # 获取客户端ID
        
        # ============ 步骤5: 验证身份信息 ============
        # 检查身份标识和客户端ID是否都存在
        if not identity or not client_id:
            # 缺少必要信息，关闭连接
            await websocket.close(code=1008, reason="Missing identity or client_id")
            return
        
        # ============ 步骤6: 存储连接 ============
        # 根据身份类型，将连接存储到对应的聊天室字典中
        if identity == "user":
            # 用户连接：创建房间（如果不存在）并存储用户连接
            chat_rooms[room_id] = chat_rooms.get(room_id, {})
            chat_rooms[room_id]["user"] = websocket
            print(f"[聊天室] 用户 {client_id} 进入房间 {room_id}")
        elif identity == "service":
            # 客服连接：创建房间（如果不存在）并存储客服连接
            chat_rooms[room_id] = chat_rooms.get(room_id, {})
            chat_rooms[room_id]["service"] = websocket
            print(f"[聊天室] 客服 {client_id} 进入房间 {room_id}")
        else:
            # 身份类型无效，关闭连接
            await websocket.close(code=1008, reason="Invalid identity")
            return
        
        # ============ 步骤7: 通知对方已进入 ============
        # 如果对方（用户或客服）已经连接，通知对方有新成员加入聊天室
        if identity == "user" and "service" in chat_rooms.get(room_id, {}):
            # 用户进入，通知已连接的客服
            await send_json(chat_rooms[room_id]["service"], {
                "type": "user_joined",  # 消息类型：用户加入
                "data": {"user_id": client_id}  # 用户ID
            })
        elif identity == "service" and "user" in chat_rooms.get(room_id, {}):
            # 客服进入，通知已连接的用户
            await send_json(chat_rooms[room_id]["user"], {
                "type": "service_joined",  # 消息类型：客服加入
                "data": {"service_id": client_id}  # 客服ID
            })
        
        # ============ 步骤8: 消息循环（核心功能） ============
        # 持续接收客户端发送的消息，直到连接断开
        while True:
            # 接收客户端发送的文本消息（阻塞等待）
            data = await websocket.receive_text()
            # 解析JSON格式的消息
            message = json.loads(data)
            # 获取消息类型，默认为 "chat"
            message_type = message.get("type", "chat")
            
            # ============ 处理聊天消息 ============
            if message_type == "chat":
                # 提取消息内容
                content = message.get("content", "")  # 消息文本内容
                sender = message.get("sender", client_id)  # 发送者ID，默认使用client_id
                timestamp = datetime.now().isoformat()  # 生成时间戳
                # 保留消息来源标识（是否来自人工客服）
                # 这个标识用于前端区分消息来源，可能用于显示不同的样式
                from_human_service = message.get("fromHumanService", False)
                
                # ============ 构建完整的聊天消息对象 ============
                chat_message = {
                    "type": "chat",  # 消息类型：聊天消息
                    "content": content,  # 消息内容
                    "sender": sender,  # 发送者ID
                    "timestamp": timestamp,  # 时间戳
                    "room_id": room_id,  # 房间ID
                    "fromHumanService": from_human_service  # 消息来源标识
                }
                
                # ============ 实时推送：转发消息给对方 ============
                # 根据当前身份，将消息实时转发给聊天室中的另一方
                if identity == "user" and "service" in chat_rooms.get(room_id, {}):
                    # 如果是用户发送的消息，且客服已连接，则实时转发给客服
                    await send_json(chat_rooms[room_id]["service"], chat_message)
                    print(f"[实时推送] 用户 {sender} 的消息已实时推送给客服，内容: {content}")  # 日志输出
                elif identity == "service" and "user" in chat_rooms.get(room_id, {}):
                    # 如果是客服发送的消息，且用户已连接，则实时转发给用户
                    await send_json(chat_rooms[room_id]["user"], chat_message)
                    print(f"[实时推送] 客服 {sender} 的消息已实时推送给用户，内容: {content}")  # 日志输出
                else:
                    # 对方未连接，记录警告
                    print(f"[警告] 房间 {room_id} 中对方未连接，消息无法实时推送，内容: {content}")  # 日志输出
                
                # 打印日志，方便调试和监控
                print(f"[聊天消息] 房间 {room_id} - {sender}: {content}")
            





    
    # ============ 异常处理：WebSocket断开 ============
    except WebSocketDisconnect:
        # 客户端主动断开连接或网络异常断开
        # 清理该连接相关的资源
        if room_id in chat_rooms and identity and client_id:
            # 根据身份类型，从聊天室中移除对应的连接
            if identity == "user":
                chat_rooms[room_id].pop("user", None)  # 移除用户连接
            elif identity == "service":
                chat_rooms[room_id].pop("service", None)  # 移除客服连接
            
            # 如果聊天室为空（没有连接了），删除整个聊天室
            if not chat_rooms[room_id]:
                chat_rooms.pop(room_id, None)
        
        # 打印断开日志
        print(f"[聊天室断开] {client_id or 'Unknown'} 离开房间 {room_id}")
    
    # ============ 异常处理：其他异常 ============
    except Exception as e:
        # 处理其他类型的异常（如JSON解析错误、网络错误等）
        print(f"[聊天室错误] 房间 {room_id}: {e}")
        
        # 即使出现异常，也要清理连接资源
        if room_id in chat_rooms and identity:
            if identity == "user":
                chat_rooms[room_id].pop("user", None)
            elif identity == "service":
                chat_rooms[room_id].pop("service", None)
















# 注册路由
app.include_router(auth_router)
app.include_router(interests_router)
app.include_router(search_router)
app.include_router(content_router)
app.include_router(goods_router)
app.include_router(orders_router)
app.include_router(dingtalk_oauth_router)
app.include_router(identity_router)
app.include_router(coze_router)
app.include_router(order_queue_router)
app.include_router(payment.router)

# 注册多轮对话系统路由
try:
    from qwen.chat_router import router as chat_router
    app.include_router(chat_router)
    print("[OK] 多轮对话系统路由已注册")
except ImportError as e:
    print(f"[警告] 多轮对话系统路由注册失败: {e}")
except Exception as e:
    print(f"[警告] 多轮对话系统路由注册失败: {e}")

# WebSocket聊天功能已注册（在 view/websocket.py 中实现）
print("[OK] WebSocket聊天功能已注册（view/websocket.py）")









@app.get("/")
async def root():
    return {
        "message": "欢迎使用小红书App API",
        "version": "1.0.0",
        "database": settings.DB_NAME
    }


@app.get("/health")
async def health_check(db: Session = Depends(get_db)):
    """健康检查接口"""
    try:
        # 测试数据库连接
        user_count = db.query(User).count()
        content_count = db.query(Content).count()
        
        return {
            "status": "healthy",
            "database": "connected",
            "user_count": user_count,
            "content_count": content_count
        }
    except Exception as e:
        import traceback
        error_msg = str(e)
        print(f"❌ Health check error: {error_msg}")
        traceback.print_exc()
        return {
            "status": "unhealthy",
            "error": error_msg,
            "type": type(e).__name__
        }


@app.get("/hello/{name}")
async def say_hello(name: str):
    return {"message": f"Hello {name}"}

